package com.learn.elasticsearch.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.learn.elasticsearch.domain.ArchivesRecord;
import com.learn.elasticsearch.enums.EsEnum;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.learn.elasticsearch.service.BaseSearchService;
import com.mate.cloud.core.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@RequiredArgsConstructor
@Service
public class BaseSearchServiceImpl <T> implements BaseSearchService<T> {





    @Qualifier("client")
    private final RestHighLevelClient restHighLevelClient;




    @Override
    public void deleteIndex(String indexName) {

    }


    /**
     * 全字段模糊匹配 （高亮显示）
     *
     * @param keyword
     * @param pageNo
     * @param pageSize
     * @return
     * @throws IOException
     */
    @Override
    public Page<ArchivesRecord> simpleSearchHeightLight(String keyword, long pageNo, long pageSize) {
        // 转码 UTF-8
        keyword = URLDecoder.decode(keyword, Charset.defaultCharset());
        // 条件搜索
        SearchRequest searchRequest = new SearchRequest(EsEnum.INDEX_NAME.getValue());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 分页
        searchSourceBuilder.from((int) pageNo);
        searchSourceBuilder.size((int) pageSize);

        // 排序
        searchSourceBuilder.sort("CREATE_TIME", SortOrder.DESC);

        QueryBuilder simpleQueryBuilder = QueryBuilders.simpleQueryStringQuery(keyword);
        searchSourceBuilder.query(simpleQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("ARCHIVES_NAME").field("ARCHIVES_CODE")
                .field("BELONG_PERSON").field("BELONG_TEL").field("DEPT_NAME")
                .field("DATA_STATUS_NAME").field("SECRET_NAME").field("ARCHIVES_TYPE")
                .field("CREATE_PERSON_NAME").field("TYPE_NAME");
        // 多个高亮显示 要设为 false
        highlightBuilder.requireFieldMatch(false);
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);

        // 执行搜索
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = null;
        List<Map<String, Object>> list = Lists.newArrayList();
        Page<ArchivesRecord> archivesRecordPage = new Page<>();
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            // 获取检索结果总数
            int count = (int) hits.getTotalHits().value;
            archivesRecordPage.setTotal(count);
            if (count != 0) {
                for (SearchHit hit : hits.getHits()) {
                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                    Map<String, HighlightField> highlightFieldMap = hit.getHighlightFields();

                    // 获取高亮
                    for (Map.Entry<String, HighlightField> entry : highlightFieldMap.entrySet()) {
                        Text[] texts = highlightFieldMap.get(entry.getKey()).getFragments();
                        String title = StringUtils.EMPTY;
                        for (Text text : texts) {
                            title += text;
                        }
                        sourceAsMap.put(entry.getKey(), title);
                    }
                    list.add(sourceAsMap);
                }
            }
            //restHighLevelClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        archivesRecordPage.setRecords(mapToBean(list));
        return archivesRecordPage;

    }


    /**
     * 全字段模糊匹配
     *
     * @param keyword
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public Page<ArchivesRecord> simpleSearch(String keyword, long pageNo, long pageSize) {
        // 转码 UTF-8
        //keyword = URLDecoder.decode(keyword, Charset.defaultCharset());
        // 条件搜索
        SearchRequest searchRequest = new SearchRequest(EsEnum.INDEX_NAME.getValue());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 分页
        //searchSourceBuilder.from((int) pageNo);
        //searchSourceBuilder.size((int) pageSize);

        // 排序
        //searchSourceBuilder.sort("CREATE_TIME", SortOrder.DESC);

        // 必须条件
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("DEL_FLAG", "0");
        // 多字段模糊条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.multiMatchQuery(keyword, "ARCHIVES_NAME", "ARCHIVES_CODE",
                "BELONG_PERSON", "BELONG_TEL", "DEPT_NAME", "DATA_STATUS_NAME","REAL_NAME",
                "SECRET_NAME", "ARCHIVES_TYPE", "CREATE_PERSON_NAME", "TYPE_NAME"));

        boolQueryBuilder.must(termQueryBuilder);

        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));

        // 执行搜索
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = null;
        List<Map<String, Object>> list = Lists.newArrayList();
        Page<ArchivesRecord> archivesRecordPage = new Page<>();
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            // 获取检索结果总数
            int count = (int) hits.getTotalHits().value;
            archivesRecordPage.setTotal(count);
            if (count != 0) {
                for (SearchHit hit : hits.getHits()) {
                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();

                    list.add(sourceAsMap);
                }
            }
            //restHighLevelClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        archivesRecordPage.setSize((int) pageSize);
        archivesRecordPage.setRecords(mapToBean(list));
        return archivesRecordPage;

    }

    /**
     *  即时搜索
     * @param keyword
     * @return
     */
    @Override
    public Set<Map<String, Object>> realTimeSearch(String keyword) {

        // 条件搜索
        SearchRequest searchRequest = new SearchRequest(EsEnum.INDEX_NAME.getValue());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 分页
        //searchSourceBuilder.from(1);
        //searchSourceBuilder.size(100);

        // 返回指定字段
        searchSourceBuilder.fetchSource("ARCHIVES_NAME",null);

        // 必须条件
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("DEL_FLAG", "0");
        // 多字段模糊条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(termQueryBuilder);
        boolQueryBuilder.must(QueryBuilders.wildcardQuery("ARCHIVES_NAME.keyword","*" + keyword + "*"));
        //boolQueryBuilder.should(QueryBuilders.matchPhrasePrefixQuery("ARCHIVES_CODE",keyword));
        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));

        // 执行搜索
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = null;

        Set<Map<String, Object>> set = Sets.newHashSet();
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();
            // 获取检索结果总数
            int count = (int) hits.getTotalHits().value;
            if (count != 0) {
                for (SearchHit hit : hits.getHits()) {
                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                    set.add(sourceAsMap);
                }
            }
            //restHighLevelClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return set;

    }


    /**
     * 查询所有数据
     *
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public Page<ArchivesRecord> searchAll(long pageNo, long pageSize) {
        // 转码 UTF-8
        //keyword = URLDecoder.decode(keyword, Charset.defaultCharset());
        // 条件搜索
        SearchRequest searchRequest = new SearchRequest(EsEnum.INDEX_NAME.getValue());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 分页
        searchSourceBuilder.from((int) pageNo);
        searchSourceBuilder.size((int) pageSize);

        // 排序
        searchSourceBuilder.sort("CREATE_TIME", SortOrder.DESC);

        QueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        searchSourceBuilder.query(matchAllQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));

        // 执行搜索
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = null;
        List<Map<String, Object>> list = Lists.newArrayList();
        Page<ArchivesRecord> archivesRecordPage = new Page<>();
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            // 获取检索结果总数
            int count = (int) hits.getTotalHits().value;
            archivesRecordPage.setTotal(count);
            if (count != 0) {
                for (SearchHit hit : hits.getHits()) {
                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                    list.add(sourceAsMap);
                }
            }
            //restHighLevelClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        archivesRecordPage.setRecords(mapToBean(list));
        return archivesRecordPage;

    }


    /**
     * 将 Map 转化为 对象
     *
     * @param mapList
     * @return
     */
    private List<ArchivesRecord> mapToBean(List<Map<String, Object>> mapList) {
        // 设置别名，用于对应bean的字段名
        Map<String, String> mapping = Maps.newHashMap();
        ArrayList<ArchivesRecord> beanList = Lists.newArrayList();
        mapping.put("ID", "id");
        mapping.put("ARCHIVES_CODE", "archivesCode");
        mapping.put("ARCHIVES_NAME", "archivesName");
        mapping.put("ARCHIVES_TYPE", "archivesType");
        mapping.put("BELONG_PERSON", "belongPerson");
        mapping.put("BELONG_PERSON_ID_CARD", "belongPersonIdCard");
        mapping.put("BELONG_TEL", "belongTel");
        mapping.put("DEL_FLAG", "delFlag");
        mapping.put("CREATE_PERSON", "createPerson");
        mapping.put("CREATE_TIME", "createTime");
        mapping.put("DEPT_ID", "deptId");
        mapping.put("DATA_STATUS", "dataStatus");
        mapping.put("SECRET_LEVAL", "secretLeval");
        mapping.put("TYPE_NAME", "typeName");
        mapping.put("BELONG_PERSON_NAME", "belongPersonName");
        mapping.put("CREATE_PERSON_NAME", "createPersonName");
        mapping.put("DEPT_NAME", "deptName");
        mapping.put("DATA_STATUS_NAME", "dataStatusName");
        mapping.put("SECRET_NAME", "secretName");
        mapping.put("ENTITY_FLAG", "entityFlag");
        mapping.put("FILE_NUM", "fileNum");
        mapping.put("ORIGIN", "origin");
        mapping.put("SCAN_FILE_NUMBER", "scanFileNumber");
        mapping.put("CHANGE_STATUS", "changeStatus");
        mapping.put("REAL_NAME", "realName");
        mapList.forEach(item -> {
            ArchivesRecord archivesRecord = BeanUtil.mapToBean(item, ArchivesRecord.class, CopyOptions.create().setFieldMapping(mapping));
            beanList.add(archivesRecord);
        });
        return beanList;
    }


    /**
     * 根据时间范围查找
     *
     * @param startTime
     * @param endTime
     * @return
     * @throws IOException
     */
    public SearchHits searchByTimeRange(String startTime, String endTime) throws IOException {
        SearchRequest searchRequest = new SearchRequest(EsEnum.INDEX_NAME.getValue());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        //queryBuilder.must(QueryBuilders.rangeQuery("CREATE_TIME").gt(startTime).lt(endTime));
        queryBuilder.must(QueryBuilders.rangeQuery("CREATE_TIME").from(startTime).to(endTime));

        searchSourceBuilder.query(queryBuilder);

        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits searchHits = searchResponse.getHits();
        return searchHits;
    }

}
